package sim;


import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.Set;

public class World 
{
	private Set<Blob> addedBlobs;
	private Set<Blob> removedBlobs;
	private Set<Blob> blobs;
	private Set<Food> foods;
	private Set<Food> removedFood;
	private Blob playerBlob;
	private long genID;
	private Vec2 upperLeft;
	private Vec2 lowerRight;
	private double timeFlowSpeed;
	public World()
	{
		timeFlowSpeed = 1.0;
		blobs = new HashSet<>();
		addedBlobs = new HashSet<>();
		removedBlobs = new HashSet<>();
		foods = new HashSet<>();
		removedFood = new HashSet<>();
		upperLeft = new Vec2(0,0);
		lowerRight = new Vec2(0,0);
		Vec2 worldCenter = new Vec2(200,200); //change this so it isn't hard coded
	
		blobs.add(new Blob(Vec2.randomVec2(200).Add(worldCenter),400, new AI(1000.0,4,1.2)));
		blobs.add(new Blob(Vec2.randomVec2(200).Add(worldCenter),400, new AI(500.0,3,1.2)));
		blobs.add(new Blob(Vec2.randomVec2(200).Add(worldCenter),400, new AI(300.0,2,1.2)));
		blobs.add(new Blob(Vec2.randomVec2(200).Add(worldCenter),400, new AI(450.0,3,1.2)));
		blobs.add(new Blob(Vec2.randomVec2(200).Add(worldCenter),400, new AI(2000.0,2,1.2)));		
		blobs.add(new Blob(new Vec2(30,200),400, new AI(2000.0,2,1.2)));		
		blobs.add(new Blob(new Vec2(150,100),400, new AI(2000.0,2,1.2)));		
	}
	
	public void setEnvSize(Vec2 upperLeft, Vec2 lowerRight) 
	{
		this.upperLeft = upperLeft;
		this.lowerRight = lowerRight;
	}
	
	public void addBlob(Blob blob)
	{
		addedBlobs.add(blob);
	}

	public void removeBlob(Blob blob)
	{
		removedBlobs.add(blob);
	}
	
	public void update(double timeStep)
	{
		timeStep *= timeFlowSpeed;
		for(Food food:foods)
		{
			if(food.getArea() <=0)
			{
				removedFood.add(food);				
			}
		}
		for(Blob blob:blobs)
		{
			if(blob.getArea() < Blob.minBlobSize)
			{
				removedBlobs.add(blob);
				continue;
			}
			blob.changeArea(timeStep * blob.getGrowthRate());
		
		}
		for(Blob blob:blobs)
		{	
			if(blob.getAI().shouldReproduce(blob) && blob.getArea() > Blob.minBlobSize)
			{
				removedBlobs.add(blob);
				Blob[] newBlobs = blob.getAI().reproduce(blob);
				for(int i=0;i<newBlobs.length; i++)
				{
					addedBlobs.add(newBlobs[i]);
				}
			}
		}
		for(Blob blob:blobs)
		{
			Vec2 preferenceVector =new Vec2(0,0);
			for(Blob otherBlob:blobs)
			{
				if (blob == otherBlob)
				{
					continue;
				}
				Vec2 blobVector = blob.getAI().desiredBlobVec2(blob, otherBlob);
				preferenceVector = preferenceVector.Add(blobVector);
			}
			for(Food food:foods)
			{
				Vec2 foodVector = blob.getAI().desiredFoodVec2(blob, food);
				preferenceVector = preferenceVector.Add(foodVector);
			}
			blob.setPreferenceVector(preferenceVector);
		}		
		for(Blob blob:blobs)
		{ 
			blob.movePreference(timeStep);
		}
		for(Blob blob:addedBlobs)
		{ 
			blobs.add(blob);
		}
		for(Blob blob:removedBlobs)
		{
			blobs.remove(blob);
		}
		for(Food food:removedFood)
		{
			foods.remove(food);
		}
		removedFood.clear();
		addedBlobs.clear();
		removedBlobs.clear();
		feedBlobs();
		blobInteractions();
	}
	
	private void feedBlobs()
	{
		for(Blob blob:blobs)
		{
			for (Food food:foods)
			{
				if (blob.isEating(food))
				{
					blob.eat(food);
				}
			}
		}
	}
	private void blobInteractions()
	{
		for(Blob blob:blobs)
		{
			blob.setProcessed(false);
		}
		for(Blob blob:blobs)
		{
			blob.setProcessed(true);
			for(Blob otherBlob:blobs)
			{
				if(!otherBlob.isProcessed() && blob.haveCollided(otherBlob))
				{
					Blob deadBlob = blob.fight(otherBlob);
					if(deadBlob!=null)
					{
						removedBlobs.add(deadBlob);
					}
					
				}
			}
		}		
	}

	public void draw(Graphics2D g2d)
    {
        g2d.setColor(new Color(0,0,0));
        g2d.fillRect((int)upperLeft.x, (int)upperLeft.y, (int)lowerRight.x, (int)lowerRight.y);
        
		for (Food food:foods)
		{
			food.draw(g2d);
		}
		for(Blob blob:blobs)
		{
            blob.draw(g2d);
        }
    }
	public void setTimeFlowSpeed(double timeFlowSpeed) 
	{
		this.timeFlowSpeed = timeFlowSpeed;
	}
	public Blob getBlobAtPosition(double x, double y) 
	{
		for(Blob blob:blobs)
		{
			if(blob.containsPoint(x,y))
			{
				return blob;
			}
		}
		return null;
	}
	public void placeFood(Vec2 clusterCenterPoint)
	{
		Random random = new Random();
		int amountOfFood = random.nextInt(5)+2;
		for (int i=0;i<amountOfFood;i++)
			{
			Vec2 position = Vec2.Add(clusterCenterPoint, Vec2.randomVec2(50));
			Food food=new Food(position);
			foods.add(food);
			}
	}
}